#include "format_fs.h"

#include "../types.h"
#include "../param.h"
#include "../fs.h"
#include "../defs.h"
#include "../fs.h"

#include "../virtio_disk.h"

#define NINODES 200


//nmeta 46 (boot(0), super(1), log blocks 30 inode blocks 13, bitmap blocks 1) blocks 1954 total 2000
static uint format_sb(struct superblock * sb){
  uint nlog = LOGSIZE;
  uint ninodeblocks = NINODES / IPB + 1;
  uint nbitmap = FSSIZE / BPB + 1;

  sb->magic = FSMAGIC;
  sb->size = FSSIZE;
  sb->nblocks = FSSIZE - 1 -1 - nlog - ninodeblocks - nbitmap;
  sb->ninodes = NINODES; //200
  sb->nlog = nlog; //
  sb->logstart = 2;
  sb->inodestart =  2 + nlog;
  sb->bmapstart = 2 + nlog + ninodeblocks;

  uint blockstart = 2 + nlog + ninodeblocks + nbitmap;

  printf("magic:0x%x, mbr:[0-0:1:0x%04x], n_sb:[1-1:1:0x%04x], log[%u-%u:%u:0x%04x], inode[%u-%u:%u:0x%04x], bmpstart:[%u-%u:%u:0x%04x],ndata_blocks:[%u-%u:%u:0x%04x]\n", 
    FSMAGIC, 0, 1<<10,
    sb->logstart, sb->logstart + sb->nlog - 1, sb->nlog, sb->logstart<<10, 
    sb->inodestart, sb->inodestart + ninodeblocks - 1, ninodeblocks, sb->inodestart << 10,
    sb->bmapstart, sb->bmapstart + nbitmap - 1, nbitmap,  sb->bmapstart <<10,
    blockstart, blockstart + sb->nblocks -1,  sb->nblocks, blockstart << 10
  );

  struct buf b;
  b.blockno = 1;
  memset(b.data, 0, BSIZE);
  memmove(b.data, sb, sizeof(*sb));
  virtio_disk_write(&b);

  return blockstart;
}

static void winode(uint inum, struct dinode * dip, struct superblock * sb){
  struct buf b;
  b.blockno = IBLOCK(inum, *sb);
  //printf("blockno:%d, inum:%d, sizeof(dinode):%ld\n", b.blockno, inum, sizeof(*dip));
  virtio_disk_read(&b);
  ((struct dinode*)b.data)[inum % IPB] = *dip;
  virtio_disk_write(&b);
}

static void rinode(uint inum, struct dinode * dip, struct superblock * sb) {
  struct buf b;
  b.blockno = IBLOCK(inum, *sb);
  virtio_disk_read(&b);
  *dip = ((struct dinode*)b.data)[inum % IPB];

}

static uint format_root_directory(uint freeblock, struct superblock * sb){
  //malloc inode for rootid
  struct dinode di;
  memset(&di, 0, sizeof(di));
  di.type = T_DIR;
  di.nlink = 1;
  di.size = 0;
  //write to disk
  winode(ROOTINO, &di, sb);

  //alloc an empty block for root_dir 's first block
  struct buf b;
  b.blockno = freeblock++;
  virtio_disk_read(&b);

  //write "." to root_dir 's block
  struct dirent de;
  de.inum = ROOTINO;
  strncpy(de.name, ".", DIRSIZE);
  memcpy(b.data, &de, sizeof(de));

  //write ".." to root_dir 's block
  de.inum = ROOTINO;
  strncpy(de.name, "..", DIRSIZE);
  memcpy(((struct dirent*)b.data) + 1, &de, sizeof(de));
  //commit the first data block for root_dir
  virtio_disk_write(&b);
  
  //write size & addrs for root inode
  di.addrs[0] = b.blockno;
  di.size += 2 * sizeof(de);
  winode(ROOTINO, &di, sb);

  print_disk_inode(&di);

  return freeblock;
}

static void format_bitmap(uint freeblock, struct superblock * sb){
  struct buf b;
  b.blockno = sb->bmapstart;
  virtio_disk_read(&b);
  for(int i = 0; i < freeblock; ++i){
    b.data[i/8] |= 1 << (i%8);
  }
  virtio_disk_write(&b);
}

void format_fs(void){
  virtio_disk_zero__();

  struct superblock sb;

  uint freeblock = format_sb(&sb);

  freeblock = format_root_directory(freeblock, &sb);

  format_bitmap(freeblock, &sb);
}

